package data;

import java.lang.reflect.Field;
import java.util.HashMap;

import main.Core;

/**
 * An abstract representation of any object the editor handles.
 * 
 * @author Richard
 */
public abstract class EditorObject {

	/*
	 * The name of the editor object. References a DataObject. All data for this
	 * object is stored in this external object. Access from the ObjectDatabase.
	 */
	protected String guid;

	/*
	 * Defines default values for desired attributes. Populated in the
	 * initDefaults() method.
	 */
	protected HashMap<String, Object> defaults;

	/**
	 * Default constructor. For subclass initialization, use the init method.
	 */
	public EditorObject() {
		this.defaults = new HashMap<String, Object>();
	}

	/**
	 * Accessor for this editor object's guid.
	 * 
	 * @return String The guid.
	 */
	public String getGUID() {
		return this.guid;
	}

	/**
	 * Only called from the object database when creating a new editor object.
	 * Do not override this method.
	 * 
	 * @param guid
	 *            The id guid.
	 */
	public void setup(String guid) {
		// Set this name attribute
		this.guid = guid;
		// Setup defaults list
		this.initDefaultData();
	}

	/*
	 * Retrieves the data object from the object database that matches this
	 * editor object.
	 */
	public synchronized DataObject getData() {
		DataObject data = ObjectDatabase.getInstance().getDataObject(
				this.getClass().getCanonicalName(), this.guid);
		return data;
	}

	/*
	 * Set up the default data for this class. Only called when creating a brand
	 * new editor object.
	 */
	protected abstract void initDefaultData();

	/**
	 * Acts as the constructor for this class.
	 */
	public abstract void init();

	/**
	 * Ensures that the data object contains all fields that the editor object
	 * subclass does.
	 */
	public void syncWithDataObject() {
		DataObject data = this.getData();
		for (String s : defaults.keySet()) {
			if (!data.containsKey(s)) {
				data.put(s, defaults.get(s));
			}
		}
	}

	/**
	 * Populates the instance variable fields with current data from the
	 * matching DataObject.
	 */
	protected void pullFields() {
		for (Field f : this.getClass().getDeclaredFields()) {
			try {
				f.set(this, this.getData().get(f.getName()));
			} catch (IllegalArgumentException e) {
				Core.getInstance()
						.logMessage(
								EditorObject.class.getName(),
								"Illegal argument when pulling field "
										+ f.getName(), e);
			} catch (IllegalAccessException e) {
				Core.getInstance().logMessage(EditorObject.class.getName(),
						"Illegal access when pulling field " + f.getName(), e);
			}
		}
	}

	/**
	 * Saves data from the instance variable fields to the attributes in the
	 * matching DataObject.
	 */
	protected void pushFields() {
		for (Field f : this.getClass().getDeclaredFields()) {
			try {
				this.getData().put(f.getName(), f.get(this));
			} catch (IllegalArgumentException e) {
				Core.getInstance()
						.logMessage(
								EditorObject.class.getName(),
								"Illegal argument when pushing field "
										+ f.getName(), e);
			} catch (IllegalAccessException e) {
				Core.getInstance().logMessage(EditorObject.class.getName(),
						"Illegal access when pushing field " + f.getName(), e);
			}
		}
	}

	/**
	 * Returns the string display for an editor object. Prints the data hash
	 * map.
	 * 
	 * @return String The string representation.
	 */
	public String toString() {
		return this.getData().toString();
	}

}
